package com.ssm.redis.util;

import com.ssm.redis.exception.ShareLockException;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;

@Component
public class RedisShareRedLock {

    @Autowired
    private RedisUtil redisUtil;

    private Long OUT_TIME = 1000L; //10s
    /**
     * @param lockKey 锁的key
     * @param requestId 锁的标识，类似线程的threadID， 删锁时判断是否为自己的锁
     *                  防止误删(业务1未执行完，锁自动删除，业务二获取锁，此时业务1执行完，删锁时把业务2的锁删了)
     * @param time
     * @return
     */
    public boolean lock(String lockKey, String requestId, Long time) {
        //1.参数校验
        if(StringUtils.isBlank(lockKey) || StringUtils.isBlank(requestId) || time <= 0) {
            //commons.lang包中的isBlank会判断null和空字符串两种情况
            throw new ShareLockException("分布式锁-加锁参数异常"); // 自定义异常
        }

        //2.加锁可自旋(一定时间内可重复获取锁)
        long currentTime = System.currentTimeMillis(); //获取当前毫秒数
        long outTime = currentTime + OUT_TIME;
        Boolean result = false;

        while(currentTime < outTime) {
            //3.利用redis的setnx设置锁
            result = redisUtil.setNx(lockKey, requestId, time, TimeUnit.MILLISECONDS);

            if(result) { //为true获取锁成功
                return result;
            }

            //获取失败，等待100ms重新尝试
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            currentTime = System.currentTimeMillis();
        }

        return result;
    }

    public boolean unLock(String lockKey, String requestId) {
        if(StringUtils.isBlank(lockKey) || StringUtils.isBlank(requestId)) {
            throw new ShareLockException("分布式锁-解锁参数异常");
        }

        try {
            //获取当前锁的值
            String value = redisUtil.get(lockKey);
            if(requestId.equals(value)) {
                //是自己的锁，删除
                boolean del = redisUtil.del(lockKey);
                return true;
            }
        } catch (Exception e) {
            //补日志
        }

        return false;
    }

    public boolean tryLock(String lockKey, String requestId, Long time) {
        //只是尝试获取锁，没有自旋操作

        if(StringUtils.isBlank(lockKey) || StringUtils.isBlank(requestId) || time <= 0) {
            throw new ShareLockException("分布式锁-加锁参数异常");
        }

        return redisUtil.setNx(lockKey, requestId, time, TimeUnit.MILLISECONDS);
    }

}
